package com.wei.web.middleware.retry.compensate.aspect;

import com.google.common.base.Joiner;
import com.tuya.biz.platform.framework.compensate.CompensateService;
import com.tuya.biz.platform.framework.compensate.annotation.Retry;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.time.Instant;
import java.util.Arrays;
import java.util.Objects;

import static com.tuya.biz.platform.framework.compensate.CompensateService.CLASS_METHOD_SEP;

/**
 * @author wujun
 */
@Aspect
@Slf4j
public class RetryAspect {
    private final CompensateService compensateService;

    public RetryAspect(CompensateService compensateService) {
        this.compensateService = compensateService;
    }


    @Pointcut("@annotation(com.tuya.biz.platform.framework.compensate.annotation.Retry) ")
    public void retryPointcut() {
        // Method is empty as this is just a Pointcut, the implementations are in the advices.
    }

    /**
     * @param joinPoint
     * @return
     */
    @Around("retryPointcut() ")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        log.debug("Enter retry pocess: {} . {} () with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));

        try {
            Object proceed = joinPoint.proceed();
            return proceed;
        } catch (Exception e) {
            Boolean first = RetryContext.isFirst();
            if (first) {
                Object[] args = joinPoint.getArgs();
                String ending = String.valueOf(Instant.now().toEpochMilli());

                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Method method = signature.getMethod();
                Retry annotation = method.getAnnotation(Retry.class);

                if (!annotation.paramCanRepeat() && args.length > 0) {
                    ending = String.valueOf(Objects.hash(args));
                }
                String key = Joiner.on(":").join(joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName(), ending);
                String classWithMethod = Joiner.on(CLASS_METHOD_SEP).join(joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());

                int retry = annotation.retry();
                Integer timesRequired = retry != 0 ? retry : null;
                failSave(key, joinPoint.getArgs(), classWithMethod, timesRequired);
            }
            throw e;
        }
    }

    private void failSave(String key, Object[] data, String classWhitMethod, Integer timesRequired) {
        try {
            compensateService.saveTask(key, data, classWhitMethod, timesRequired);
        } catch (Exception e) {
            log.error("structureDO error;key={},data={},class={}", key, data, this.getClass(), e);
        }
    }


}
